{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "谭清文 18\n",
      "我是谭清文, 我今年18岁\n",
      "谭清文 20\n",
      "我是谭清文, 我今年20岁\n"
     ]
    }
   ],
   "source": [
    "# 定义一个类\n",
    "class Parent:\n",
    "\n",
    "    def __init__(self):\n",
    "        self.name = \"谭清文\"\n",
    "        self.age = 18\n",
    "\n",
    "    def say(self):  # 定义一个类的方法,可被外部调用\n",
    "        print(f\"我是{self.name}, 我今年{self.age}岁\")\n",
    "\n",
    "\n",
    "p1 = Parent()  # 实例化一个类. 不实例化的话将不可用\n",
    "print(p1.name, p1.age)\n",
    "p1.say()\n",
    "\n",
    "p2 = Parent()\n",
    "p2.age = 20\n",
    "print(p2.name, p2.age)\n",
    "p2.say()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "new\n",
      "init\n",
      "谭清文 18\n",
      "我是谭清文, 我今年18岁\n",
      "new\n",
      "init\n",
      "丁小格 20\n",
      "我是丁小格, 我今年20岁\n"
     ]
    }
   ],
   "source": [
    "# 通过外部参数实例化\n",
    "class Parent:\n",
    "\n",
    "    # 这是构造函数\n",
    "    def __init__(self, name):\n",
    "        print(\"init\")\n",
    "        self.name = name  # 通过外部传参进行初始化\n",
    "        self.age = 18\n",
    "\n",
    "    def say(self):\n",
    "        print(f\"我是{self.name}, 我今年{self.age}岁\")\n",
    "\n",
    "\n",
    "p1 = Parent(\"谭清文\")\n",
    "print(p1.name, p1.age)\n",
    "p1.say()\n",
    "\n",
    "p2 = Parent(\"丁小格\")\n",
    "p2.age = 20\n",
    "print(p2.name, p2.age)\n",
    "p2.say()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Parent:\n",
    "\n",
    "    # 初始化方法\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.age = 18\n",
    "\n",
    "    def say(self):\n",
    "        print(f\"我是{self.name}, 我今年{self.age}岁\")\n",
    "\n",
    "\n",
    "p1 = Parent(\"谭清文\")\n",
    "p1.height = 179  # type: ignore # 可以单独给一个类创建一个属性, 并赋值. 就像创建一个变量一样随意\n",
    "print(p1.height)  # type: ignore"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "56\n",
      "100\n"
     ]
    }
   ],
   "source": [
    "# 私有属性\n",
    "class Parent:\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.age = 18\n",
    "        self.__kg: int = 56  # 两个下划线,将属性设为私有. 只能通过对外的接口(方法)去修改\n",
    "\n",
    "    def say(self):\n",
    "        print(f\"我是{self.name}, 我今年{self.age}岁\")\n",
    "\n",
    "    def getKg(self) -> int:\n",
    "        return self.__kg\n",
    "\n",
    "    def setKg(self, inKg):\n",
    "        if isinstance(inKg, int):  # 封装成接口后, 可以在修改体重前做数据类型判断\n",
    "            self.__kg = inKg\n",
    "        else:\n",
    "            print(f\"Error: Persion SetKG\")\n",
    "\n",
    "\n",
    "p1 = Parent(\"谭清文\")\n",
    "# print(p1.__kg)  # 会报错\n",
    "print(p1.getKg())  # 可以通过定义的接口方法去读取\n",
    "\n",
    "p1.setKg(100)\n",
    "print(p1.getKg())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Persion' object has no attribute '__say'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[1], line 16\u001b[0m\n\u001b[1;32m     12\u001b[0m         \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m我是\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m, 我今年\u001b[39m\u001b[38;5;132;01m{\u001b[39;00m\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mage\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m岁\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m     15\u001b[0m p1 \u001b[38;5;241m=\u001b[39m Persion(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m谭清文\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m---> 16\u001b[0m \u001b[43mp1\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__say\u001b[49m()  \u001b[38;5;66;03m# 会报错\u001b[39;00m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Persion' object has no attribute '__say'"
     ]
    }
   ],
   "source": [
    "# 私有方法\n",
    "class Parent:\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.age = 18\n",
    "        self.__kg: int = 56\n",
    "\n",
    "    # 两个下划线,将方法设为私有, 只能间接调用\n",
    "    # 适合作为内部的通用子函数\n",
    "    def __say(self):\n",
    "        print(f\"我是{self.name}, 我今年{self.age}岁\")\n",
    "\n",
    "\n",
    "p1 = Parent(\"谭清文\")\n",
    "p1.__say()  # 会报错"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "new\n",
      "init\n",
      "del\n"
     ]
    }
   ],
   "source": [
    "# 自动的魔法方法\n",
    "# 前后双下划线的方法为魔法方法,会在特定阶段自动执行,不需要手动执行\n",
    "class Parent:\n",
    "\n",
    "    def __new__(\n",
    "        cls, *args, **kwargs\n",
    "    ):  # 不加*args和**kwargs,会导致调用__init__的时候异常\n",
    "        print(\"new\")\n",
    "        return object.__new__(cls)  # 这里不return的话, 就会跳过__init__方法\n",
    "\n",
    "    # 这是构造函数\n",
    "    def __init__(self, name):\n",
    "        print(\"init\")\n",
    "        self.name = name  # 通过外部传参进行初始化\n",
    "        self.age = 18\n",
    "\n",
    "    def __del__(self):\n",
    "        print(\"del\")\n",
    "\n",
    "\n",
    "p1 = Parent(\"谭清文\")  # 初始化的时候, __new__会在__init__之前\n",
    "del p1  # 销毁的时候会调用__del__方法"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.PersionA object at 0x1033eaa00>\n",
      "<__main__.PersionA object at 0x1033eaa00>\n",
      "定义了__str__\n"
     ]
    }
   ],
   "source": [
    "# 手动魔法方法\n",
    "class PersionA:\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.age = 18\n",
    "\n",
    "\n",
    "class PersionB:\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "        self.age = 18\n",
    "\n",
    "    def __str__(self):\n",
    "        return \"定义了__str__\"\n",
    "\n",
    "\n",
    "p1 = PersionA(\"A\")\n",
    "print(p1)\n",
    "print(\n",
    "    p1.__str__()\n",
    ")  # 会打印<__main__.PersionA object at 0x103085580>, 就跟直接打印这个实例一样\n",
    "\n",
    "\n",
    "p2 = PersionB(\"B\")\n",
    "print(p2.__str__())  # PersionB额外写了__str__方法, 再调用的时候就会返回指定的内容"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "ename": "ValueError",
     "evalue": "'name' in __slots__ conflicts with class variable",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mValueError\u001b[0m                                Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[4], line 3\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;66;03m# slots可以限制动态添加属性\u001b[39;00m\n\u001b[1;32m      2\u001b[0m \u001b[38;5;66;03m# 目前的python版本好像不能这么写\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m \u001b[38;5;28;01mclass\u001b[39;00m \u001b[38;5;21;01mPersion\u001b[39;00m:\n\u001b[1;32m      4\u001b[0m     name \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m      5\u001b[0m     \u001b[38;5;18m__slots__\u001b[39m \u001b[38;5;241m=\u001b[39m (\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mname\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mage\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
      "\u001b[0;31mValueError\u001b[0m: 'name' in __slots__ conflicts with class variable"
     ]
    }
   ],
   "source": [
    "# slots可以限制动态添加属性\n",
    "# 目前的python编辑器不会自动补全__slots__\n",
    "class Parent:\n",
    "    name = \"\"\n",
    "    __slots__ = (\"name\", \"age\")\n",
    "\n",
    "\n",
    "p1 = Parent()\n",
    "p1.weight = 16  # 会被限制添加\n",
    "print(p1.weight)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "谭清文今年18岁\n",
      "谭清文是程序员, 会唱歌\n"
     ]
    }
   ],
   "source": [
    "# 继承\n",
    "class Parent:\n",
    "\n",
    "    def __init__(self, name=\"谭清文\", age=18) -> None:\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    def say(self):\n",
    "        print(f\"{self.name}今年{self.age}岁\")\n",
    "\n",
    "\n",
    "class Child(Parent):  # 通过括号内添加类名, 继承另一个类\n",
    "    def sing(self):\n",
    "        print(f\"{self.name}是程序员, 会唱歌\")\n",
    "\n",
    "\n",
    "p1 = Child()\n",
    "p1.say()\n",
    "p1.sing()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "谭清文 大西瓜\n"
     ]
    }
   ],
   "source": [
    "# 子类属性会覆盖父类同名属性\n",
    "class Parent:\n",
    "\n",
    "    def __init__(self, name=\"谭清文\", age=18) -> None:\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "\n",
    "    def say(self):\n",
    "        print(f\"{self.name}今年{self.age}岁\")\n",
    "\n",
    "\n",
    "class Mid(Parent):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Child(Parent):\n",
    "    def __init__(self):\n",
    "        self.name = \"大西瓜\"\n",
    "\n",
    "\n",
    "c1 = Mid()\n",
    "c2 = Child()\n",
    "print(c1.name, c2.name)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "ename": "AttributeError",
     "evalue": "'Child' object has no attribute '__weight'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[6], line 17\u001b[0m\n\u001b[1;32m     13\u001b[0m     \u001b[38;5;28;01mpass\u001b[39;00m\n\u001b[1;32m     16\u001b[0m c1 \u001b[38;5;241m=\u001b[39m Child()\n\u001b[0;32m---> 17\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[43mc1\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m__weight\u001b[49m) \u001b[38;5;66;03m# 这样写会报错\u001b[39;00m\n",
      "\u001b[0;31mAttributeError\u001b[0m: 'Child' object has no attribute '__weight'"
     ]
    }
   ],
   "source": [
    "# 私有属性不会被继承\n",
    "class Parent:\n",
    "\n",
    "    def __init__(self, name=\"谭清文\", age=18) -> None:\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        self.__weight = 30\n",
    "\n",
    "\n",
    "class Child(Parent):\n",
    "    pass\n",
    "\n",
    "\n",
    "c1 = Child()\n",
    "print(c1.__weight)  # 这样写会报错"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "True\n",
      "True\n"
     ]
    }
   ],
   "source": [
    "# 子类或多重子类, 可以通过isinstance判断是否属于某个父类\n",
    "class Parent:\n",
    "\n",
    "    def __init__(self, name=\"谭清文\", age=18) -> None:\n",
    "        self.name = name\n",
    "        self.age = age\n",
    "        self.__weight = 30\n",
    "\n",
    "\n",
    "class Mid(Parent):\n",
    "    pass\n",
    "\n",
    "\n",
    "class Child(Mid):\n",
    "    pass\n",
    "\n",
    "\n",
    "p1 = Parent()\n",
    "m1 = Mid()\n",
    "c2 = Child()\n",
    "\n",
    "print(isinstance(p1, Parent))\n",
    "print(isinstance(m1, Parent))\n",
    "print(isinstance(c2, Parent))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "P1\n",
      "P2\n"
     ]
    }
   ],
   "source": [
    "# 测试-多父类同名方法的优先级\n",
    "class P1:\n",
    "    def say(self):\n",
    "        print(\"P1\")\n",
    "\n",
    "\n",
    "class P2:\n",
    "    def say(self):\n",
    "        print(\"P2\")\n",
    "\n",
    "\n",
    "class C1(P1, P2):\n",
    "    pass\n",
    "\n",
    "\n",
    "class C2(P2, P1):\n",
    "    pass\n",
    "\n",
    "\n",
    "c1 = C1()\n",
    "c1.say()\n",
    "\n",
    "c2 = C2()\n",
    "c2.say()  # 先写的类的方法,会优先继承"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n",
      "3 3 3 3\n"
     ]
    }
   ],
   "source": [
    "# 静态属性\n",
    "class Persion:\n",
    "    count = 0  # 属性写在__init__外面, 这样就是一个静态属性\n",
    "\n",
    "    def __init__(self):\n",
    "        Persion.count += 1  # 这样可以在每次初始化一个实例时,增加静态属性count的计数\n",
    "\n",
    "\n",
    "print(Persion.count)  # 没有写self的属性就是静态属性可以直接打印,不需要实例化\n",
    "\n",
    "p1 = Persion()\n",
    "print(Persion.count)\n",
    "\n",
    "p2 = Persion()\n",
    "print(Persion.count)\n",
    "\n",
    "p3 = Persion()\n",
    "print(\n",
    "    Persion.count, p1.count, p2.count, p3.count\n",
    ")  # 实例也会共享静态属性的值. 可以通过设置静态属性做到一改全改"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我被实例化0次\n",
      "我被实例化1次\n",
      "我被实例化2次\n",
      "我被实例化3次\n",
      "我被实例化4次\n"
     ]
    }
   ],
   "source": [
    "# 类方法\n",
    "class Persion:\n",
    "    count = 0  # 属性写在__init__外面, 这样就是一个静态属性\n",
    "\n",
    "    def __init__(self):\n",
    "        Persion.count += 1\n",
    "\n",
    "    @classmethod  # 这样可以创建一个静态方法\n",
    "    def say(cls):  # 需要将self改成cls,cls代表这个类\n",
    "        print(f\"我被实例化{cls.count}次\")\n",
    "\n",
    "\n",
    "Persion.say()\n",
    "\n",
    "p1 = Persion()\n",
    "Persion.say()\n",
    "\n",
    "p2 = Persion()\n",
    "Persion.say()\n",
    "\n",
    "p3 = Persion()\n",
    "Persion.say()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Reading\n"
     ]
    }
   ],
   "source": [
    "# 静态方法\n",
    "# 功能会比类方法少, 需要显式传入类或实例\n",
    "# 静态方法开销与类方法没有显著区别\n",
    "class Persion:\n",
    "\n",
    "    @staticmethod  # 这样写可以创建静态方法\n",
    "    def read():  # 此时不需要传入self,也不需要传入class\n",
    "        print(\"Reading\")\n",
    "\n",
    "\n",
    "Persion.read()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.6"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
